#include <control.h>
#include <stdlib.h>

CONTROL_ERR placeHuman(human *hmn, field *fld){

  level protLvl;

  if(sameTerritoryOrBorder(hmn->territoryCapital, fld) == FALSE)
    return CONTROL_NOT_ALLOWED;

  if(fld->clr == hmn->clr)
    return placeHumanInFriendlyField(hmn, fld);

  protLvl = analyzeProtection(fld);
  if(protLvl >= hmn->lvl)
    return CONTROL_NOT_ALLOWED;

  conquestHexagon(hmn, fld);

  return CONTROL_OK;

}

CONTROL_ERR placeFort(field *capital, field *fld){

  if(sameTerritoryOrBorder(capital, fld) == FALSE)
    return CONTROL_NOT_ALLOWED;

  if(fld->clr != capital->clr)
    return CONTROL_NOT_ALLOWED;

  if(fld->isCapital == TRUE)
    return CONTROL_NOT_ALLOWED;

  if(fld->isTree == TRUE)
    return CONTROL_NOT_ALLOWED;

  if(fld->isFort == TRUE)
    return CONTROL_NOT_ALLOWED;

  if(fld->whoHere != NULL)
    return CONTROL_NOT_ALLOWED;

  fld->isFort = TRUE;
  return CONTROL_OK;

}

level analyzeProtection(field *fld){

  level ret = UNPROTECTED;
  level tst;
  int i;

  ret = returnProtector(fld);
  
  for(i=0; i<6; i++){

    if(fld->mapNeighs[i] == NULL)
      continue;

    if(fld->clr == fld->mapNeighs[i]->clr){
      if(tst = returnProtector(fld->mapNeighs[i]) > ret)
        ret = tst;
    }

  }

  return ret;

}

level returnProtector(field *fld){

  level ret = UNPROTECTED;

  if(fld != NULL){

    if(fld->isCapital == TRUE)
      ret = CAPITAL;

    else if(fld->isFort == TRUE)
      ret = FORT;

    if(fld->whoHere != NULL){
      if(fld->whoHere->lvl > ret)
        ret = fld->whoHere->lvl;
    }

  }

  return ret;

}

CONTROL_ERR placeHumanInFriendlyField(human *hmn, field *fld){

  if(fld->isCapital == TRUE)
    return CONTROL_NOT_ALLOWED;
  
  if(fld->isFort == TRUE)
    return CONTROL_NOT_ALLOWED;

  if(fld->whoHere != NULL){
    if(fld->whoHere->lvl + hmn->lvl > CHAMPION){
      return CONTROL_NOT_ALLOWED;
    }
    else{
      fld->whoHere->lvl += hmn->lvl;
      killHuman(hmn);
      return CONTROL_OK;
    }
  }
  
  if(hmn->myPlace != NULL)
    hmn->myPlace->whoHere = NULL;
  fld->isTree = FALSE;
  hmn->myPlace = fld;
  fld->whoHere = hmn;
  return CONTROL_OK;

}

bool sameTerritoryOrBorder(field *capital, field *fld){

  int i;

  if(fld->territoryCapital == capital)
    return TRUE;
  
  for(i=0; i<6; i++){

    if(fld->mapNeighs[i] == NULL)
      continue;

    if(fld->mapNeighs[i]->territoryCapital == capital)
      return TRUE;

  }

  return FALSE;

}

void conquestHexagon(human *hmn, field *fld){

  field *exCapital;
  field *newTree;
  field *hexagonNotReached;
  field *capitalAux;
  CONTROL_ERR err;
  color formerClr;
  int i;

  killHuman(fld->whoHere);

  fld->whoHere = hmn;
  exCapital = fld->territoryCapital;
  fld->territoryCapital = hmn->territoryCapital;
  formerClr = fld->clr;
  fld->clr = hmn->clr;

  if(hmn->myPlace != NULL)
    hmn->myPlace->whoHere = NULL;
  hmn->myPlace = fld;

  if(fld->isCapital == TRUE){

    for(i=0; i<6; i++){
      if(fld->territoryNeighs[i] != NULL){
        capitalAux = fld->territoryNeighs[i];
        break;
      }
    }

    executeMarking(capitalAux);
  }
  else{
    executeMarking(exCapital);
  }

  while(checkAllHexagonsInTerritory(exCapital, exCapital, &hexagonNotReached) != CONTROL_OK){
    if(buildTerritory(hexagonNotReached) == CONTROL_OK)
      addTerritory(hexagonNotReached, formerClr);
  }
  
  if(exCapital->clr == formerClr){
    buildTerritory(exCapital);
  }
  else{
    exCapital->isCapital = FALSE;
    exCapital->funds = 0;
    freeHumans(exCapital);

    capitalAux->funds = 0;

    if(buildTerritory(capitalAux) == CONTROL_OK)
      addTerritory(capitalAux, formerClr);
  }

  //Winning Team New Hexagon
  buildTerritory(hmn->territoryCapital);

  //Color Territories Consistency Routine
  adjustTerritories(formerClr);
  adjustTerritories(hmn->clr);

}

void executeMarking(field *root){

  field *fifo;
  field *prec;
  int i;
  
  fifo = root;

  while(fifo != NULL){
    fifo->ss = RED;

    for(i=0; i<6; i++){
      if(fifo->territoryNeighs[i] != NULL){
        if(fifo->territoryNeighs[i]->clr == fifo->clr){
          if(fifo->territoryNeighs[i]->ss != ORANGE && fifo->territoryNeighs[i]->ss != RED){
            fifo->territoryNeighs[i]->ss = ORANGE;
            fifo->bSearch = fifo->territoryNeighs[i];
          }
        }
      }
    }

    prec = fifo;
    fifo = fifo->bSearch;
    prec->bSearch = NULL;

  }

}

CONTROL_ERR buildTerritory(field *capital){

  field *fifo;
  field *prec;
  int i;
  bool notTerritoryFlag = TRUE;
  
  fifo = capital;

  for(i=0; i<6; i++){
    if(capital->mapNeighs[i]->clr == capital->clr)
      notTerritoryFlag = FALSE;
  }
  if(notTerritoryFlag == TRUE){
    for(i=0; i<6; i++){
      capital->territoryNeighs[i] = NULL;
    }
    capital->territoryCapital = NULL;
    capital->numberHumans = 0;
    capital->isCapital = FALSE;
    capital->funds=0;
    capital->isFort = FALSE;

    if(capital->whoHere != NULL)
      free(capital->whoHere);
    else
      freeHumans(capital);
    
    if(capital->Humans != NULL)
      free(capital->Humans);

    capital->ss = WHITE;
    capital->bSearch = NULL;
    return CONTROL_NOT_A_TERRITORY;
  }

  capital->territoryCapital = capital;
  capital->isCapital = TRUE;
  capital->isTree = FALSE;
  capital->isFort = FALSE;
  killHuman(capital->whoHere);

  while(fifo != NULL){
    fifo->ss = BLACK;

    if(fifo != capital){
      fifo->isCapital = FALSE;
      capital->funds += fifo->funds;
      fifo->funds = 0;
      if(fifo->isCapital == TRUE){
        fifo->isCapital = FALSE;
        fifo->numberHumans = 0;
        if(fifo->Humans != NULL){
          free(fifo->Humans);
          fifo->Humans = NULL;
        }
      }
    }

    fifo->territoryCapital = capital;

    for(i=0; i<6; i++){
      if(fifo->mapNeighs[i] != NULL){
        if(fifo->mapNeighs[i]->clr == fifo->clr){
          if(fifo->mapNeighs[i]->ss != GRAY && fifo->mapNeighs[i]->ss != BLACK){
            fifo->mapNeighs[i]->ss = GRAY;
            fifo->bSearch = fifo->territoryNeighs[i];
            fifo->territoryNeighs[i] = fifo->mapNeighs[i];
            fifo->mapNeighs[i]->territoryNeighs[(i+3)%6] = fifo;
          }
        }
      }
    }

    prec = fifo;
    fifo = fifo->bSearch;
    prec->bSearch = NULL;

  }

  collectHumans(capital, capital);

  clearSearchState(capital); 
  return CONTROL_OK;

}

void addTerritory(field *capital, color clr){

  field **newTerritories;
  field ***oldTerritories;
  int *territoriesNumber;
  int i;

  switch(clr){
    case(LIGHTGREEN):
      oldTerritories = &lightGreenTerritories;
      territoriesNumber = &lightGreenTerritoriesNumber;
      break;
    case(DARKGREEN):
      oldTerritories = &darkGreenTerritories;
      territoriesNumber = &darkGreenTerritoriesNumber;
      break;
    case(YELLOW):
      oldTerritories = &yellowTerritories;
      territoriesNumber = &yellowTerritoriesNumber;
      break;
    case(BROWN):
      oldTerritories = &brownTerritories;
      territoriesNumber = &brownTerritoriesNumber;
      break;
    case(BLUE):
      oldTerritories = &blueTerritories;
      territoriesNumber = &blueTerritoriesNumber;
      break;
    case(RED):
      oldTerritories = &redTerritories;
      territoriesNumber = &redTerritoriesNumber;
      break;
    case(PINK):
      oldTerritories = &pinkTerritories;
      territoriesNumber = &pinkTerritoriesNumber;
      break;
    case(BLACK):
      oldTerritories = &blackTerritories;
      territoriesNumber = &blackTerritoriesNumber;
      break;
  }

  (*territoriesNumber) += 1;
  newTerritories = (field**)malloc(sizeof(field*)*(*territoriesNumber));
  for(i=0; i < (*territoriesNumber) - 1; i++){
    newTerritories[i] = (*oldTerritories)[i];
  }
  newTerritories[i] = capital;

  free(*oldTerritories);
  (*oldTerritories) = newTerritories;

}

CONTROL_ERR checkAllHexagonsInTerritory(field *capital, field *fld, field **notReached){
  
  int i;
  
  if(fld == NULL){
    *notReached = NULL;
    return CONTROL_OK;
  }

  if(fld->territoryCapital == capital && fld->ss != RED){
    *notReached = fld;
    return CONTROL_HEXAGON_NOT_REACHED;
  }

  for(i=0; i<6; i++){
    if(checkAllHexagonsInTerritory(capital, fld->territoryNeighs[i], notReached) == CONTROL_HEXAGON_NOT_REACHED){
      return CONTROL_HEXAGON_NOT_REACHED;
    }
  }

  *notReached = NULL;
  return CONTROL_OK;

}

void adjustTerritories(color clr){

  field **newTerritories;
  field ***oldTerritories;
  int *territoriesNumber;
  int newTerritoriesNumber;
  int i, nt;

  switch(clr){
    case(LIGHTGREEN):
      oldTerritories = &lightGreenTerritories;
      territoriesNumber = &lightGreenTerritoriesNumber;
      break;
    case(DARKGREEN):
      oldTerritories = &darkGreenTerritories;
      territoriesNumber = &darkGreenTerritoriesNumber;
      break;
    case(YELLOW):
      oldTerritories = &yellowTerritories;
      territoriesNumber = &yellowTerritoriesNumber;
      break;
    case(BROWN):
      oldTerritories = &brownTerritories;
      territoriesNumber = &brownTerritoriesNumber;
      break;
    case(BLUE):
      oldTerritories = &blueTerritories;
      territoriesNumber = &blueTerritoriesNumber;
      break;
    case(RED):
      oldTerritories = &redTerritories;
      territoriesNumber = &redTerritoriesNumber;
      break;
    case(PINK):
      oldTerritories = &pinkTerritories;
      territoriesNumber = &pinkTerritoriesNumber;
      break;
    case(BLACK):
      oldTerritories = &blackTerritories;
      territoriesNumber = &blackTerritoriesNumber;
      break;
  }
  
  newTerritoriesNumber = 0;
  for(i=0; i < *territoriesNumber; i++){
    if((*oldTerritories)[i]->isCapital == TRUE)
      newTerritoriesNumber++;
  }

  newTerritories = (field**)malloc(sizeof(field*)*newTerritoriesNumber);
  nt = 0;
  for(i=0; i < (*territoriesNumber) - 1; i++){
    if((*oldTerritories)[i]->isCapital == TRUE){
      newTerritories[nt] = (*oldTerritories)[i];
      nt++;
    }
  }

  (*territoriesNumber) = newTerritoriesNumber;

  free(*oldTerritories);
  (*oldTerritories) = newTerritories; 

}

void killHuman(human *hmn){

  field *capital = hmn->territoryCapital;
  human **newHumans;
  int h, nh;

  if(hmn == NULL)
    return;

  newHumans = (human**)malloc((sizeof(human*))*(capital->numberHumans));
  nh = 0;
  for(h=0; h<capital->numberHumans; h++){
    if(capital->Humans[h] != hmn){
      newHumans[nh] = capital->Humans[h];
      nh++;
    }
    else{
      hmn->myPlace->whoHere = NULL;
      free(hmn);
    }
  }

  capital->numberHumans -= 1;

  free(capital->Humans);
  capital->Humans = newHumans;

}

void clearSearchState(field *fld){

  int i;

  if(fld == NULL)
    return;

  fld->ss = WHITE;

  for(i=0; i<6; i++){
    clearSearchState(fld->territoryNeighs[i]);
  }

}

void freeHumans(field *capital){

  int h;

  for(h=0; h < capital->numberHumans; h++){
    capital->Humans[h]->myPlace->whoHere = NULL;
    free(capital->Humans[h]);
  }

  capital->numberHumans = 0;
  free(capital->Humans);
  capital->Humans = NULL;

}

human* hireHuman(field *capital, level lvl, field *fld){

  human *newHuman;

  newHuman = (human*)malloc(sizeof(human));

  newHuman->clr = capital->clr;
  newHuman->lvl = lvl;
  newHuman->canMove = TRUE;
  newHuman->territoryCapital = capital;
  newHuman->myPlace = NULL;

  addHuman(capital, newHuman);

  capital->funds -= lvl * 10;

  placeHuman(newHuman, fld);

  return newHuman;

}

void collectHumans(field *capital, field *root){

  int i;

  if(root == NULL)
    return;

  if(root->whoHere != NULL)
    addHuman(capital, root->whoHere);

  for(i=0; i<6; i++){
    collectHumans(capital, root->territoryNeighs[i]);
  }

}

void addHuman(field *capital, human *hmn){

  capital->numberHumans += 1;
  capital->Humans = (human**)realloc(capital->Humans, sizeof(field*)*(capital->numberHumans));
  capital->Humans[capital->numberHumans - 1] = hmn;

}

